import { createVNode as _createVNode } from "vue";
import { ref, computed, watchEffect, defineComponent } from "vue";
import { clamp, numericProp, makeArrayProp, preventDefault, createNamespace, makeRequiredProp } from "../utils/index.mjs";
import { getElementTranslateY, findIndexOfEnabledOption } from "./utils.mjs";
import { useEventListener, useParent } from "@vant/use";
import { useTouch } from "../composables/use-touch.mjs";
import { useExpose } from "../composables/use-expose.mjs";
// 使用三种动画: 1.点击动画(css) 2.拖拽跟手动画 3.惯性滑动动画(css) 4.拖拽结束纠正偏移(css动画)
const DEFAULT_DURATION = 200; // 默认动画过渡时间
const MOMENTUM_TIME = 300; // 惯性移动 启动时间条件
const MOMENTUM_DISTANCE = 15; // 惯性移动 启动距离条件
const [name, bem] = createNamespace("picker-column");
const PICKER_KEY = Symbol(name);
var stdin_default = defineComponent({
  name,
  props: {
    value: numericProp,
    fields: makeRequiredProp(Object), // 数据配置数据关键字
    options: makeArrayProp(), // 列中各列数据
    readonly: Boolean, 
    allowHtml: Boolean, // 展示文字 可否设置html
    optionHeight: makeRequiredProp(Number), // 选项高度
    swipeDuration: makeRequiredProp(numericProp), // 快速滑动的惯性滚动的时长
    visibleOptionNum: makeRequiredProp(numericProp) // 可见的选项个数
  },
  emits: ["change", "clickOption", "scrollInto"],
  setup(props, {
    emit,
    slots
  }) {
    let moving; // 是否正在touchMove
    let startOffset;
    let touchStartTime;
    let momentumOffset;
    let transitionEndTrigger; // css过渡结束 回调
    const root = ref();
    const wrapper = ref();

    // 设定currentOffset
    // 1.拖拽跟手 2.惯性
    const currentOffset = ref(0); // 选择列相对 第一个选择项(居中的时候)的偏移量
    const currentDuration = ref(0);
    const touch = useTouch();

    const count = () => props.options.length;
    // 默认偏移index为0且居中展示 的偏移量, 后面的只能在此基础上向上偏移
    const baseOffset = () => props.optionHeight * (+props.visibleOptionNum - 1) / 2;

    // 通过index更新选中值 计算偏移量 更新currentOfffset值
    // 先更新currentOffset值, css结束后emit change事件
    // 1.点击选中 2.拖拽结束惯性偏移量 3.拖拽结束纠正偏移
    const updateValueByIndex = (index) => {
      let enabledIndex = findIndexOfEnabledOption(props.options, index);
      // 只会在baseOffset上 向上偏移
      const offset = -enabledIndex * props.optionHeight;
      // css过渡结束时回调
      const trigger = () => {
        if (enabledIndex > count() - 1) {
          enabledIndex = findIndexOfEnabledOption(props.options, index);
        }
        const value = props.options[enabledIndex][props.fields.value];
        if (value !== props.value) {
          emit("change", value);
        }
      };
      // 2和3时会设置突变的offset
      if (moving && offset !== currentOffset.value) {
        // 将trigger 作为结束回调
        transitionEndTrigger = trigger;
      } else {
        trigger();
      }
      // 先突变设置currentOffset, css过渡后触发change事件
      currentOffset.value = offset;
    };

    const isReadonly = () => props.readonly || !props.options.length;
    // 点击选中切换
    const onClickOption = (index) => {
      // 移动中 isReadonly 失效
      if (moving || isReadonly()) {
        return;
      }
      // 不使用transitionEndTrigger, 通过clickOption通知外部
      transitionEndTrigger = null;
      currentDuration.value = DEFAULT_DURATION;
      updateValueByIndex(index);
      emit("clickOption", props.options[index]);
    };

    // 通过移动距离计算idnex
    const getIndexByOffset = (offset) => clamp(Math.round(-offset / props.optionHeight), 0, count() - 1);
    // curretnOffset.value获取index 响应式
    const currentIndex = computed(() => getIndexByOffset(currentOffset.value));

    // 脱手惯性移动函数 distance距离, duration时间
    const momentum = (distance, duration) => {
      // speed和脱手前滑动速度有关
      const speed = Math.abs(distance / duration);
      // 默认脱手移动0.003秒
      distance = currentOffset.value + speed / 3e-3 * (distance < 0 ? -1 : 1);
      const index = getIndexByOffset(distance);
      // 拖拽结束时 非正常位置 设定默认过渡时间
      currentDuration.value = +props.swipeDuration;
      updateValueByIndex(index);
    };

    // css过渡结束 执行回调
    const stopMomentum = () => {
      moving = false;
      currentDuration.value = 0;
      if (transitionEndTrigger) {
        transitionEndTrigger();
        transitionEndTrigger = null;
      }
    };

    // 开始滑动
    const onTouchStart = (event) => {
      if (isReadonly()) {
        return;
      }
      touch.start(event);
      if (moving) {
        // 滚动中时  强制打断根据当前位置计算位置并且关闭动画
        // 惯性滚动时 可以拖拽
        const translateY = getElementTranslateY(wrapper.value);
        currentOffset.value = Math.min(0, translateY - baseOffset());
      }
      // 开始滑动时将 过渡时间设置为0 过渡回调清空
      currentDuration.value = 0;
      startOffset = currentOffset.value;
      touchStartTime = Date.now();
      // 设置惯性移动 判断量
      momentumOffset = startOffset;
      transitionEndTrigger = null;
    };

    // 滑动中
    const onTouchMove = (event) => {
      if (isReadonly()) {
        return;
      }
      touch.move(event);
      // 当为垂直方向滑动时
      if (touch.isVertical()) {
        moving = true;
        preventDefault(event, true);
      }
      // 跟手移动时 向下可拉动1个距离 向上可以拉动count个距离
      const newOffset = clamp(startOffset + touch.deltaY.value, -(count() * props.optionHeight), props.optionHeight);
      const newIndex = getIndexByOffset(newOffset);
      if (newIndex !== currentIndex.value) {
        emit("scrollInto", props.options[newIndex]);
      }
      // 拖拽时设置偏移位置
      currentOffset.value = newOffset;
      const now = Date.now();
      // 当开始和touchMove或者touchMove事件间隔太多更新判断值
      if (now - touchStartTime > MOMENTUM_TIME) {
        touchStartTime = now;
        momentumOffset = newOffset;
      }
    };

    // 滑动结束
    const onTouchEnd = () => {
      if (isReadonly()) {
        return;
      }
      const distance = currentOffset.value - momentumOffset;
      const duration = Date.now() - touchStartTime;
      // 满足惯性移动 时间间隔 移动距离要求 触发投手惯性移动
      const startMomentum = duration < MOMENTUM_TIME && Math.abs(distance) > MOMENTUM_DISTANCE;
      if (startMomentum) {
        // 有惯性移动时, 惯性移动期间 moving为true
        momentum(distance, duration);
        return;
      }
      const index = getIndexByOffset(currentOffset.value);
      currentDuration.value = DEFAULT_DURATION;
      // 滑动结束纠正css动画时间短 moving直接设置为false
      updateValueByIndex(index);
      setTimeout(() => {
        moving = false;
      }, 0);
    };

    // column中各项渲染函数
    const renderOptions = () => {
      const optionStyle = {
        height: `${props.optionHeight}px`
      };
      return props.options.map((option, index) => {
        // 获取 展示文本 绑定值 disable状态
        const text = option[props.fields.text];
        const {
          disabled
        } = option;
        const value = option[props.fields.value];
        const data = {
          role: "button",
          style: optionStyle,
          tabindex: disabled ? -1 : 0,
          class: [bem("item", {
            disabled,
            selected: value === props.value
          }), option.className],
          onClick: () => onClickOption(index)
        };
        const childData = {
          class: "van-ellipsis",
          [props.allowHtml ? "innerHTML" : "textContent"]: text
        };
        // 可使用 slots.poption自定义展示文本
        return _createVNode("li", data, [slots.option ? slots.option(option, index) : _createVNode("div", childData, null)]);
      });
    };
    useParent(PICKER_KEY);
    useExpose({
      stopMomentum
    });

    // 监听props.options外部改变时更新currentOffset.value的值
    watchEffect(() => {
      const index = moving ? Math.floor(-currentOffset.value / props.optionHeight) : props.options.findIndex((option) => option[props.fields.value] === props.value);
      const enabledIndex = findIndexOfEnabledOption(props.options, index);
      const offset = -enabledIndex * props.optionHeight;
      if (moving && enabledIndex < index)
        stopMomentum();
      currentOffset.value = offset;
    });
    useEventListener("touchmove", onTouchMove, {
      target: root
    });
    return () => _createVNode("div", {
      "ref": root,
      "class": bem(),
      "onTouchstartPassive": onTouchStart,
      "onTouchend": onTouchEnd,
      "onTouchcancel": onTouchEnd
    }, [_createVNode("ul", {
      "ref": wrapper,
      "style": {
        transform: `translate3d(0, ${currentOffset.value + baseOffset()}px, 0)`,
        transitionDuration: `${currentDuration.value}ms`,
        transitionProperty: currentDuration.value ? "all" : "none"
      },
      "class": bem("wrapper"),
      "onTransitionend": stopMomentum
    }, [renderOptions()])]);
  }
});
export {
  PICKER_KEY,
  stdin_default as default
};
